github.com/quinndk/ethereum_read@v0.0.0-20181211143958-29c55eec3237/Docs/0x07 Block.md (about) 1 # 以太坊源码研读0x07 Block 2 3 前面看了以太坊的交易模块,而交易都是要打包在区块上的。Block是Eth上存储价值信息的核心数据结构之一。 4 5 一个完整的Block大概包括以下几部分: 6 7 - 1.所有账户的相关活动,都是以Transaction格式存储,每个Block有一个Tx的列表 8 9 - 2.每个交易的执行结果,由一个Receipt对象与其包含的一组Log对象记录 10 11 - 3.所有交易执行完后生成的Receipt列表,存储在Block中(经过压缩加密) 12 13 - 4.不同Block之间,通过前向指针ParentHash一个一个串联起来成为一个单向链表,BlockChain 结构体管理着这个链表 14 15 - 5.Block结构体基本可分为Header和Body两个部分 16 17 # 废话少说撸代码 18 19 ### Block结构 20 21 ``` 22 // Block represents an entire block in the Ethereum blockchain. 23 type Block struct { 24 // 区块头 25 header *Header 26 // 叔块的区块头 27 uncles []*Header 28 // 交易列表 29 transactions Transactions 30 31 // caches 32 hash atomic.Value 33 size atomic.Value 34 35 // Td is used by package core to store the total difficulty 36 // of the chain up to and including the block. 37 // totalDifficulty 区块总难度 当前区块难度值 = td - lastBlock.td 38 td *big.Int 39 40 // These fields are used by package eth to track 41 // inter-peer block relay. 42 // 出块时间 43 ReceivedAt time.Time 44 // 区块体 45 ReceivedFrom interface{} 46 } 47 ``` 48 一个Block的唯一标识符就是它的hash,这里的hash是指其Header内容的RLP哈希值。在第一次计算后会缓存到hash值里。 49 50 ``` 51 // Hash returns the keccak256 hash of b's header. 52 // The hash is computed on the first call and cached thereafter. 53 func (b *Block) Hash() common.Hash { 54 if hash := b.hash.Load(); hash != nil { 55 return hash.(common.Hash) 56 } 57 v := b.header.Hash() 58 b.hash.Store(v) 59 return v 60 } 61 ... 62 // Hash returns the block hash of the header, which is simply the keccak256 hash of its 63 // RLP encoding. 64 func (h *Header) Hash() common.Hash { 65 return rlpHash(h) 66 } 67 .. 68 func rlpHash(x interface{}) (h common.Hash) { 69 hw := sha3.NewKeccak256() 70 rlp.Encode(hw, x) 71 hw.Sum(h[:0]) 72 return h 73 } 74 ``` 75 76 这里每一个Block都有一个BlockHeader,Header是Block的核心,它的成员变量全都是公共的,可以很方便的向调用者提供关于Block属性的操作。 77 78 ``` 79 // Header represents a block header in the Ethereum blockchain. 80 type Header struct { 81 // 父区块hash,即链上上一个区块的Hash 82 ParentHash common.Hash `json:"parentHash" gencodec:"required"` 83 // 叔块集合uncles的RLP哈希值 84 UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` 85 // 挖出区块的矿工地址 86 Coinbase common.Address `json:"miner" gencodec:"required"` 87 // MPT状态树根哈希 88 Root common.Hash `json:"stateRoot" gencodec:"required"` 89 // 交易树根节点RLP哈希值 90 TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` 91 // 收据树根节点RLP哈希值 92 ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` 93 // Bloom过滤器(Filter),用来快速判断一个参数Log对象是否存在于一组已知的Log集合中 94 Bloom Bloom `json:"logsBloom" gencodec:"required"` 95 // 区块难度 96 Difficulty *big.Int `json:"difficulty" gencodec:"required"` 97 // 区块序号,相当于Bitcoin的Height 98 Number *big.Int `json:"number" gencodec:"required"` 99 // 区块内所有Gas消耗的理论上限 100 GasLimit uint64 `json:"gasLimit" gencodec:"required"` 101 // 区块内所有Transaction执行时所实际消耗的Gas总和 102 GasUsed uint64 `json:"gasUsed" gencodec:"required"` 103 // 出块时间 104 Time *big.Int `json:"timestamp" gencodec:"required"` 105 // 额外数据 106 Extra []byte `json:"extraData" gencodec:"required"` 107 // 用于POW 108 MixDigest common.Hash `json:"mixHash" gencodec:"required"` 109 // 用于POW 结合MixDigest生成区块哈希值 110 Nonce BlockNonce `json:"nonce" gencodec:"required"` 111 } 112 ``` 113 此外,以太坊将一个Block中的交易集合和叔块集合单独封装到一个Body结构中,因为他们相对于Header需要更多的内存空间,在传输和验证时为了节省时间可以和Header分开进行。 114 115 ``` 116 // Body is a simple (mutable, non-safe) data container for storing and moving 117 // a block's data contents (transactions and uncles) together. 118 // Body可以理解为Block里的数组成员集合,它相对于Header需要更多的内存空间, 119 // 所以在数据传输和验证时,往往与Header是分开进行的。 120 type Body struct { 121 Transactions []*Transaction 122 Uncles []*Header 123 } 124 ``` 125 126 我们注意到,这里相比Bitcoin多了一个叔块(uncle)的概念。这里可以参考官方对[叔块](https://github.com/ethereum/wiki/wiki/Design-Rationale#uncle-incentivization)的解释。 127 128 叔块,顾名思义就是跟自己的父区块在一个高度上。我们知道相比Bitcoin,Eth将出块时间缩短到了15s左右。这样在庞大的P2P网络中就增大了同时出现同一高度区块的概率,这样就有可能使得大批矿工因为产生这样的区块而得不到奖励。因此,以太坊引入了叔块的概念,一个叔块该满足的条件为: 129 130 - 1.叔块必须是B的第K层祖先,2<= k <= 7 131 - 2.叔块不能是B的祖先 132 - 3.叔块必须拥有合法的block Header 133 - 4.叔块必须是不曾被包含进区块链过的 134 135 这样,以太坊的激励机制就有所改变。当一个矿工挖到一个普通区块B时,若该区块拥有叔块,该矿工将除固定区块奖励外额外再得到__固定区块奖励/32*count(uncles)__的奖励。 136 同时,曾经挖出叔块的矿工也将获得__(Number(uncle)+8-Number(B))*固定区块奖励/8__的叔块奖励。我们举个简单的小🌰: 137 138 A挖到一个Number为20的Block,该区块由两个叔块uncle1(B挖出Number=18)和uncle2(C挖出Number=13),此时的奖励分配为: 139 140 - A = 3 + 3/32*2 = 3.1875eth 141 142 - B = (18 + 8 - 20) * 3 / 8 = 3*6/8 = 2.25eth 143 144 - C = (13 + 8 - 20) * 3 / 8 = 3*1/8 = 0.375eth 145 146 关于区块奖励的源码今天不是重点,这里只是稍微提一下。 147 148 ### Block基本操作 149 150 首先来看添加新Block的操作,代码逻辑清晰简单。 151 152 ``` 153 // NewBlock creates a new block. The input data is copied, 154 // changes to header and to the field values will not affect the 155 // block. 156 // 157 // The values of TxHash, UncleHash, ReceiptHash and Bloom in header 158 // are ignored and set to values derived from the given txs, uncles 159 // and receipts. 160 func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt) *Block { 161 b := &Block{header: CopyHeader(header), td: new(big.Int)} 162 163 // TODO: panic if len(txs) != len(receipts) 164 if len(txs) == 0 { 165 b.header.TxHash = EmptyRootHash 166 } else { 167 b.header.TxHash = DeriveSha(Transactions(txs)) 168 b.transactions = make(Transactions, len(txs)) 169 copy(b.transactions, txs) 170 } 171 172 if len(receipts) == 0 { 173 b.header.ReceiptHash = EmptyRootHash 174 } else { 175 b.header.ReceiptHash = DeriveSha(Receipts(receipts)) 176 b.header.Bloom = CreateBloom(receipts) 177 } 178 179 if len(uncles) == 0 { 180 b.header.UncleHash = EmptyUncleHash 181 } else { 182 b.header.UncleHash = CalcUncleHash(uncles) 183 b.uncles = make([]*Header, len(uncles)) 184 for i := range uncles { 185 b.uncles[i] = CopyHeader(uncles[i]) 186 } 187 } 188 189 return b 190 } 191 192 // NewBlockWithHeader creates a block with the given header data. The 193 // header data is copied, changes to header and to the field values 194 // will not affect the block. 195 func NewBlockWithHeader(header *Header) *Block { 196 return &Block{header: CopyHeader(header)} 197 } 198 ``` 199 200 至此,关于Block数据结构的源码就看完了。 201 202 203 204 205 206